/**
 * @file    list_timer.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   List timer.
 * @version 0.1
 * @date    2025-03-18
 *
 * @copyright Copyright (c) 2025
 * */
#include "list_timer.h"

struct list_timer {
    uint32_t alarm;
    uint32_t period;
    listimer_cb_t callback;        //!< Function to call when timer expires
    void* arg;                       //!< Argument to pass to the callback
    LIST_ENTRY(list_timer) list_entry;
};

// lists of currently armed timers for two dispatch methods: ISR and TASK
static LIST_HEAD(list_name, list_timer) s_timers = LIST_HEAD_INITIALIZER(s_timers);

static void timer_insert(listimer_handle_t timer)
{
    listimer_handle_t it, last = NULL;
    if (LIST_FIRST(&s_timers) == NULL) {
        LIST_INSERT_HEAD(&s_timers, timer, list_entry);
    } else {
        LIST_FOREACH(it, &s_timers, list_entry) {
            if (timer->alarm < it->alarm) {
                LIST_INSERT_BEFORE(it, timer, list_entry);
                break;
            }
            last = it;
        }
        if (it == NULL) {
            LIST_INSERT_AFTER(last, timer, list_entry);
        }
    }
    if (timer == LIST_FIRST(&s_timers)) {
        //timer_set_alarm_id(first_timer->alarm);
    }
}


void listimer_create(listimer_handle_t *timer, listimer_cb_t callback, void *arg)
{
    listimer_handle_t result = (listimer_handle_t) calloc(1, sizeof(*result));
    if (result == NULL) return;

    result->callback = callback;
    result->arg = arg;
    *timer = result;
}

void listimer_start(listimer_handle_t timer, uint32_t timeout, bool repetitive) // @suppress("Type cannot be resolved")
{
    if (timer == NULL || timeout == 0) return;

    listimer_stop(timer);  /* Temporarily set the timer and then reset the timer */
    timer->alarm  = listimer_get_time() + timeout;
    timer->period = repetitive ? timeout : 0;
    timer_insert(timer);
}

void listimer_stop(listimer_handle_t timer)
{
    if (timer == NULL) return;
    /* Check if the timer is armed once the list is locked to avoid a data race */
    if (timer->alarm > 0) {
        LIST_REMOVE(timer, list_entry);
        timer->alarm = 0;
        timer->period = 0;
    }
}

void listimer_delete(listimer_handle_t timer)
{
    if (timer == NULL) return;

    /* Check if the timer is armed once the list is locked to avoid a data race */
    if (timer->alarm > 0) {
        LIST_REMOVE(timer, list_entry);
        free(timer);
        timer = NULL;
    }
}

void listimer_deinit(void)
{
    LIST_EMPTY(&s_timers); // @suppress("Statement has no effect")
}

void listimer_process(void)
{
    uint32_t now = listimer_get_time();
    listimer_handle_t it = LIST_FIRST(&s_timers);
    if (it == NULL || it->alarm > now) {
        return;
    }
    LIST_REMOVE(it, list_entry);
    if (it->period > 0) {
        it->alarm += it->period;
        timer_insert(it);
    } else {
        it->alarm = 0;
    }
    it->callback(it->arg);
}


#if 0
void listimer_cb(void* arg)
{
    LOGI("listimer....%s", (char *)arg);
}
void listimer_test(void)
{
    listimer_handle_t timer0;
    listimer_create(&timer0, listimer_cb, "timer0");
    listimer_start(timer0, 1000, 1);
    while (1) { listimer_process() };
}
#endif
